package com.qinglei.recoup.common.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qinglei.recoup.common.domain.ActiveUser;
import com.qinglei.recoup.common.domain.CommonConstant;
import com.qinglei.recoup.common.exception.RedisConnectException;
import com.qinglei.recoup.common.service.CacheService;
import com.qinglei.recoup.common.service.RedisService;
import com.qinglei.recoup.common.utils.JsonUtils;
import com.qinglei.recoup.system.dao.UserMapper;
import com.qinglei.recoup.system.domain.Dict;
import com.qinglei.recoup.system.domain.DictBO;
import com.qinglei.recoup.system.domain.Menu;
import com.qinglei.recoup.system.domain.Role;
import com.qinglei.recoup.system.domain.User;
import com.qinglei.recoup.system.enums.RoleEnum;
import com.qinglei.recoup.system.service.DictService;
import com.qinglei.recoup.system.service.MenuService;
import com.qinglei.recoup.system.service.RoleService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

@Service("cacheService")
@Slf4j
public class CacheServiceImpl implements CacheService {

    @Autowired
    private RedisService redisService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private MenuService menuService;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private ObjectMapper mapper;

    @Resource
    private DictService dictService;


    @Override
    public void testConnect() throws Exception {
        this.redisService.exists("test");
    }

    @Override
    public User getUser(Long userId) throws Exception {
        String userString = this.redisService.get(CommonConstant.USER_CACHE_PREFIX + userId);
        if (StringUtils.isBlank(userString))
            throw new Exception();
        else
            return this.mapper.readValue(userString, User.class);
    }

    @Override
    public List<Role> getRoles(Long userId) throws Exception {
        String roleListString = this.redisService.get(CommonConstant.USER_ROLE_CACHE_PREFIX + userId);
        if (StringUtils.isBlank(roleListString)) {
            throw new Exception();
        } else {
            JavaType type = mapper.getTypeFactory().constructParametricType(List.class, Role.class);
            return this.mapper.readValue(roleListString, type);
        }
    }

    @Override
    public List<Menu> getPermissions(Long userId) throws Exception {
        String permissionListString = this.redisService.get(CommonConstant.USER_PERMISSION_CACHE_PREFIX + userId);
        if (StringUtils.isBlank(permissionListString)) {
            throw new Exception();
        } else {
            JavaType type = mapper.getTypeFactory().constructParametricType(List.class, Menu.class);
            return this.mapper.readValue(permissionListString, type);
        }
    }

    @Override
    public void saveUser(User user) throws Exception {
        Long userId = user.getId();
        this.deleteUser(userId);
        redisService.set(CommonConstant.USER_CACHE_PREFIX + userId, mapper.writeValueAsString(user));
    }

    @Override
    public void saveUser(Long userId) throws Exception {
        User user = userMapper.findDetailById(userId);
        this.deleteUser(userId);
        redisService.set(CommonConstant.USER_CACHE_PREFIX + userId, mapper.writeValueAsString(user));
    }

    @Override
    public void saveRoles(Long userId) throws Exception {
        List<Role> roleList = this.roleService.findUserRole(userId);
        if (!roleList.isEmpty()) {
            this.deleteRoles(userId);
            redisService.set(CommonConstant.USER_ROLE_CACHE_PREFIX + userId, mapper.writeValueAsString(roleList));
        }
    }

    @Override
    public void savePermissions(Long userId) throws Exception {
        List<Menu> permissionList = this.menuService.findUserPermissions(userId);
        if (!permissionList.isEmpty()) {
            this.deletePermissions(userId);
            redisService.set(CommonConstant.USER_PERMISSION_CACHE_PREFIX + userId, mapper.writeValueAsString(permissionList));
        }
    }

    @Override
    public void deleteUser(Long userId) throws Exception {
        redisService.del(CommonConstant.USER_CACHE_PREFIX + userId);
    }

    @Override
    public void deleteRoles(Long userId) throws Exception {
        redisService.del(CommonConstant.USER_ROLE_CACHE_PREFIX + userId);
    }

    @Override
    public void deletePermissions(Long userId) throws Exception {
        redisService.del(CommonConstant.USER_PERMISSION_CACHE_PREFIX + userId);
    }

    /**
     * 获取当前在线护工用户
     *
     * @return
     */
    @Override
    public List<ActiveUser> getOnlineNurse() throws RedisConnectException {
        Set<String> strSet = this.redisService.zrangeByScore(CommonConstant.ACTIVE_USERS_ZSET_PREFIX, "-inf", "+inf");
        List<ActiveUser> userList = strSet.stream().map(item -> {
            try {
                Long userId = Long.valueOf(item);
                ActiveUser onlineUser = this.getOnlineUser(userId);
                return onlineUser;
            } catch (Exception e) {
                log.error("getOnlineNurse error", e);
            }
            return null;
        })
                .filter(Objects::nonNull)
                .filter(item -> String.valueOf(RoleEnum.NURSE.getCode()).equals(item.getRoleId()))
                .collect(Collectors.toList());
        return userList;
    }

    @Override
    public ActiveUser getOnlineUser(Long userId) throws RedisConnectException {
        String activeUserStr = redisService.get(CommonConstant.USER_TOKEN_CACHE_PREFIX + userId);
        if (StringUtils.isEmpty(activeUserStr)) {
            return null;
        }
        ActiveUser activeUser = JsonUtils.jsonToBean(activeUserStr, ActiveUser.class);
        return activeUser;
    }

    @Override
    public void saveDictItemGroup(String itemGroup) throws RedisConnectException {
        List<Dict> dictList = dictService.list(Wrappers.<Dict>lambdaQuery().eq(Dict::getItemGroup, itemGroup));
        if (dictList != null) {
            this.deleteDictItemGroup(itemGroup);
            Map<String, String> dictMap = dictList.stream().map(item -> {
                DictBO dictBO = new DictBO();
                BeanUtils.copyProperties(item, dictBO);
                return dictBO;
            }).collect(Collectors.toMap(item -> String.valueOf(item.getKeyy()), JsonUtils::objectToJson));
            redisService.hmset(CommonConstant.DICT_ITEM_CACHE_PREFIX + itemGroup, dictMap);
        }
    }

    @Override
    public void deleteDictItemGroup(String itemGroup) throws RedisConnectException {
        redisService.del(CommonConstant.DICT_ITEM_CACHE_PREFIX + itemGroup);
    }

    @Override
    public DictBO getDictItem(String itemGroup, Long key) throws RedisConnectException {
        List<String> hmget = redisService.hmget(CommonConstant.DICT_ITEM_CACHE_PREFIX + itemGroup, String.valueOf(key));
        if (CollectionUtils.isEmpty(hmget)) {
            return null;
        }
        return JsonUtils.jsonToBean(hmget.get(0), DictBO.class);
    }

    @Override
    public List<DictBO> getDictItemList(String itemGroup) throws RedisConnectException {
        Map<String, String> map = redisService.hgetAll(CommonConstant.DICT_ITEM_CACHE_PREFIX + itemGroup);
        if (MapUtils.isEmpty(map)) {
            return null;
        }
        List<DictBO> dictList = new ArrayList<>();
        map.forEach((key, str) -> {
            DictBO dict = JsonUtils.jsonToBean(str, DictBO.class);
            if (dict != null) {
                dictList.add(dict);
            }
        });
        dictList.sort((o1, o2) -> (int) (o1.getKeyy() - o2.getKeyy()));
        return dictList;
    }

//    @Override
//    public void saveOutBedTimeout(Integer val) throws RedisConnectException {
//        int outBedTimeout = systemConfigService.getOutBedTimeout();
//        redisService.set(CommonConstant.CACHE_OUT_BED_TIMEOUT, String.valueOf(outBedTimeout));
//    }

//    @Override
//    public void saveTakeEventTimeout(Integer tenantId, Integer val) throws RedisConnectException {
//        redisService.hset(CommonConstant.TAKE_EVENT_TIMEOUT_CACHE_PREFIX, String.valueOf(tenantId), String.valueOf(val));
//    }

//    @Override
//    public Integer getOutBedTimeout() throws RedisConnectException {
//        Integer ret = null;
//        String val = redisService.get(CommonConstant.CACHE_OUT_BED_TIMEOUT);
//        if (val != null) {
//            ret = Integer.valueOf(val);
//        }
//        return ret;
//    }

//    @Override
//    public Integer getTakeEventTimeout(Integer tenantId) throws RedisConnectException {
//        Integer ret = null;
//        String val = redisService.hget(CommonConstant.TAKE_EVENT_TIMEOUT_CACHE_PREFIX, String.valueOf(tenantId));
//        if (val != null) {
//            ret = Integer.valueOf(val);
//        }
//        return ret;
//    }

}